/*
 *Author: Abdul Bezrati
 *Email : abezrati@hotmail.com
 */

#ifndef TUPLE2_H
#define TUPLE2_H
#include "../TypeDef.h"

template <class T2>
class Tuple2
{
  public:
    _HOST_DEVICE_ Tuple2(T2 X = 0, T2 Y = 0)
    {
      x = X;
      y = Y;
    }

	_HOST_DEVICE_ Tuple2 Normalize()
	{
		float length = sqrtf(x*x + y*y)+0.00001;
		return Tuple2(x/length, y/length);
	};

    _HOST_DEVICE_ Tuple2(const Tuple2<T2> &source)
    {
      x = source.x;
      y = source.y;
    }

    _HOST_DEVICE_ Tuple2<T2> &operator = (const Tuple2<T2> &right)
	{
      x = right.x;
      y = right.y;
      return *this;
    }

    _HOST_DEVICE_ inline Tuple2<T2> operator + (const Tuple2<T2> &right)
    {
      return Tuple2<T2>(right.x + x, right.y + y);
    }

	_HOST_DEVICE_ inline Tuple2<T2> operator * (const Tuple2<T2> &right)
	{
		return Tuple2<T2>(right.x * x, right.y * y);
	}

    _HOST_DEVICE_ inline Tuple2<T2> operator - (const Tuple2<T2> &right)
	{
      return Tuple2<T2>(-right.x + x, -right.y + y);
    }

	_HOST_DEVICE_ inline float operator | (const Tuple2<T2> &right)
	{
		return x*right.x + y*right.y;
	}

   _HOST_DEVICE_ inline Tuple2<T2> operator * (float scale)
	{
      return Tuple2<T2>(x*scale, y*scale);
    }

    _HOST_DEVICE_ inline Tuple2<T2>  operator / (const T2 scale)
	{
      if(scale) 
        return Tuple2<T2>(x/scale, y/scale);
      return Tuple2<T2>(0, 0);
    }

    _HOST_DEVICE_ inline Tuple2<T2> &operator += (const Tuple2<T2> &right)
    {
      x+=right.x;
      y+=right.y;
      return *this;
    }

    _HOST_DEVICE_ inline Tuple2<T2> &operator -= (const Tuple2<T2> &right)
    {
      x-=right.x;
      y-=right.y;	
      return *this;
    }

    _HOST_DEVICE_ inline Tuple2<T2> &operator *= (const T2 scale)
    {
      x*=scale;
      y*=scale;
      return *this;
    }

    _HOST_DEVICE_ inline Tuple2<T2> &operator /= (const T2 scale)
	{
      if(scale)
      {
        x /=scale;
        y /=scale;
      }
      return *this;
    }

    _HOST_DEVICE_ inline operator const T2*() const { return &x; }
   _HOST_DEVICE_  inline operator T2*()             { return &x; }   

	  inline const T2  operator[](int i) const { return ((T2*)&x)[i]; }
	  inline       T2 &operator[](int i)       { return ((T2*)&x)[i]; }

    _HOST_DEVICE_ bool operator == (const Tuple2<T2> &right)
    {
      //return (x == right.x &&
              //y == right.y);
		return fabs(float(right.x-x))<0.00001 && fabs(float(right.y-y))<0.00001;
    }

    _HOST_DEVICE_ bool operator != (const Tuple2<T2> &right)
    {
		return fabs(float(right.x-x))>=0.00001 || fabs(float(right.y-y))>=0.00001;
		//return !(fabs(float(right.x-x))<0.00001 || fabs(float(right.y-y))<0.00001);
    }

    void set(T2 nx, T2 ny)
    {
      x = nx;
      y = ny;
    }

	_HOST_DEVICE_ inline float getLength()
	{
		return sqrtf(GetLengthSqurted());
	}

	_HOST_DEVICE_ inline Tuple2<T2> &normalize()
	{
		T2 length  = getLength();

		if(!length)
		{
			set(0,0);
			return *this;
		}
		x/=length;
		y/=length;
		return *this;
	}

    _HOST_DEVICE_ inline void clamp(T2 min, T2 max)
    {
      x = x > max ? max : x < min ? min  : x;
      y = y > max ? max : y < min ? min  : y;
    }

	_HOST_DEVICE_ inline T2 GetLengthSqurted()
	{
		return x*x+y*y;
	}

    T2 x, y;
};

typedef Tuple2<int   > Tuple2i;
typedef Tuple2<float > Tuple2f;
typedef Tuple2<double> Tuple2d;
#endif
